package VLine

import (
	"bufio"
	"fmt"
	"math"
	"math/rand"
	"os"
	"time"
)

type Instruction struct {
	index int
	code  int
	args  []int
}

var Base = map[string]*Table{
	"line": (func() *Table {
		var table = TableNew()
		table.Put("range", func(line *Line, self interface{}, args []interface{}) interface{} {
			switch len(args) {
			case 1:
				return &LoopRange{0, int(args[0].(float64)), 1, true}
			case 2:
				if int(args[0].(float64)) < int(args[1].(float64)) {
					return &LoopRange{int(args[0].(float64)), int(args[1].(float64)), 1, true}
				} else {
					return &LoopRange{int(args[0].(float64)), int(args[1].(float64)), -1, false}
				}
			case 3:
				return &LoopRange{int(args[0].(float64)), int(args[1].(float64)), int(args[2].(float64)), int(args[2].(float64)) > 0}
			}
			return nil
		})
		return table
	})(),
	"os": (func() *Table {
		var table = TableNew()
		table.Put("write", func(line *Line, self interface{}, args []interface{}) interface{} {
			for i := 0; i < len(args); i++ {
				fmt.Print(args[i])
			}
			return nil
		})
		scanner := bufio.NewScanner(os.Stdin)
		table.Put("read", func(line *Line, self interface{}, args []interface{}) interface{} {
			scanner.Scan()
			return scanner.Text()
		})
		table.Put("now", func(line *Line, self interface{}, args []interface{}) interface{} {
			return (float64)(time.Now().UnixNano() / 1e6)
		})
		return table
	})(),
	"math": (func() *Table {
		var table = TableNew()
		var rand = rand.New(rand.NewSource(time.Now().UnixNano()))
		table.Put("rand", func(line *Line, self interface{}, args []interface{}) interface{} {
			return rand.Float64()
		})
		table.Put("abs", func(line *Line, self interface{}, args []interface{}) interface{} {
			return math.Abs(args[0].(float64))
		})
		return table
	})(),
	"table": (func() *Table {
		var table = TableNew()
		table.Put("push", func(line *Line, self interface{}, args []interface{}) interface{} {
			var table = args[0].(*Table)
			for i := 1; i < len(args); i++ {
				table.list.Push(args[i])
			}
			return table
		})
		return table
	})(),
	"string": (func() *Table {
		var table = TableNew()
		table.Put("table", func(line *Line, self interface{}, args []interface{}) interface{} {
			var runes = []rune(args[0].(string))
			var table = TableNew()
			for i := 0; i < len(runes); i++ {
				table.list.Push(string(runes[i]))
			}
			return table
		})
		return table
	})(),
}

const (
	VM_LOAD = iota
	VM_MOV
	VM_INHEAP
	VM_OUTHEAP
	VM_NEWTABLE
	VM_GETTV
	VM_SETTV
	VM_POS
	VM_NEG
	VM_ADD
	VM_SUB
	VM_MUL
	VM_DIV
	VM_MOD
	VM_POWER
	VM_UNM
	VM_NOT
	VM_EQ
	VM_NEQ
	VM_LE
	VM_LT
	VM_AND
	VM_OR
	VM_JUMP
	VM_CONCAT
	VM_RETURN
	VM_CALL
	VM_CALLX
	VM_PUSH
	VM_IMPORT
	VM_CLOSURE
	VM_GETUPVAL
	VM_SETUPVAL
	VM_LOOP
	VM_FORIN
)

func InstructionNew(index int, vm int, args []int) *Instruction {
	return &Instruction{index, vm, args}
}

func (instruction *Instruction) GetArgs() []int {
	return instruction.args
}

func (instruction *Instruction) SetArgs(index int, value int) {
	instruction.args[index] = value
}
func (instruction *Instruction) SetIndex(index int) {
	instruction.index = index
}

func (instruction *Instruction) String() string {
	var name string
	switch instruction.code {
	case VM_LOAD:
		name = "VM_LOAD"
	case VM_MOV:
		name = "VM_MOV"
	case VM_INHEAP:
		name = "VM_INHEAP"
	case VM_OUTHEAP:
		name = "VM_OUTHEAP"
	case VM_NEWTABLE:
		name = "VM_NEWTABLE"
	case VM_GETTV:
		name = "VM_GETTV"
	case VM_SETTV:
		name = "VM_SETTV"
	case VM_POS:
		name = "VM_POS"
	case VM_NEG:
		name = "VM_NEG"
	case VM_ADD:
		name = "VM_ADD"
	case VM_SUB:
		name = "VM_SUB"
	case VM_MUL:
		name = "VM_MUL"
	case VM_DIV:
		name = "VM_DIV"
	case VM_MOD:
		name = "VM_MOD"
	case VM_POWER:
		name = "VM_POWER"
	case VM_UNM:
		name = "VM_UNM"
	case VM_NOT:
		name = "VM_NOT"
	case VM_EQ:
		name = "VM_EQ"
	case VM_NEQ:
		name = "VM_NEQ"
	case VM_LE:
		name = "VM_LE"
	case VM_LT:
		name = "VM_LT"
	case VM_AND:
		name = "VM_AND"
	case VM_OR:
		name = "VM_OR"
	case VM_JUMP:
		name = "VM_JUMP"
	case VM_CONCAT:
		name = "VM_CONCAT"
	case VM_RETURN:
		name = "VM_RETURN"
	case VM_CALL:
		name = "VM_CALL"
	case VM_CALLX:
		name = "VM_CALLX"
	case VM_PUSH:
		name = "VM_PUSH"
	case VM_IMPORT:
		name = "VM_IMPORT"
	case VM_CLOSURE:
		name = "VM_CLOSURE"
	case VM_GETUPVAL:
		name = "VM_GETUPVAL"
	case VM_SETUPVAL:
		name = "VM_SETUPVAL"
	case VM_LOOP:
		name = "VM_LOOP"
	case VM_FORIN:
		name = "VM_FORIN"
	}
	return fmt.Sprint(instruction.index, " ", name, instruction.args)
}
